﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SharpSoft.WPF.Controls
{
    /// <summary>
    /// 爆炸动画效果,将一个物体炸碎后粒子动画移动到新的位置
    /// </summary>
    public class BoomAnimationControl : MechanicsCanvas
    {
        static BoomAnimationControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(BoomAnimationControl), new FrameworkPropertyMetadata(typeof(BoomAnimationControl)));
        }
        public BoomAnimationControl()
        {
            this.Loaded += BoomAnimationControl_Loaded;
        }

        private void BoomAnimationControl_Loaded(object sender, RoutedEventArgs e)
        {
            resetGrid();
        }

        /// <summary>
        /// 承载的图形
        /// </summary>
        public Visual Visual
        {
            get { return (Visual)GetValue(VisualProperty); }
            set { SetValue(VisualProperty, value); }
        }

        public static readonly DependencyProperty VisualProperty =
            DependencyProperty.Register("Visual", typeof(Visual), typeof(BoomAnimationControl), new PropertyMetadata(null));


        /// <summary>
        /// 分解的列数
        /// (实测分解的粒子个数不宜超过1200，即<see cref="Columns"/>*<see cref="Rows"/>不要超过1200，否则可能会卡顿。)
        /// </summary>
        public int Columns
        {
            get { return (int)GetValue(ColumnsProperty); }
            set { SetValue(ColumnsProperty, value); }
        }

        public static readonly DependencyProperty ColumnsProperty =
            DependencyProperty.Register("Columns", typeof(int), typeof(BoomAnimationControl), new PropertyMetadata(0));


        /// <summary>
        /// 分解的行数
        /// (实测分解的粒子个数不宜超过1200，即<see cref="Columns"/>*<see cref="Rows"/>不要超过1200，否则可能会卡顿。)
        /// </summary>
        public int Rows
        {
            get { return (int)GetValue(RowsProperty); }
            set { SetValue(RowsProperty, value); }
        }

        public static readonly DependencyProperty RowsProperty =
            DependencyProperty.Register("Rows", typeof(int), typeof(BoomAnimationControl), new PropertyMetadata(0));

        /// <summary>
        /// 新位置，一个向量，表示从当前位置偏移。
        /// </summary>
        public Vector NewPlace { get; set; }
        /// <summary>
        /// 爆炸速度
        /// </summary>
        public double BoomVelocity { get; set; } = 1000;
        /// <summary>
        /// 爆炸的随机性
        /// </summary>
        public double BoomRandomSeed { get; set; } = 1.3d;
        /// <summary>
        /// 将粒子拖向目标点的力
        /// </summary>
        public double BaseTargetForce { get; set; } = 1200;
         

        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            base.OnRenderSizeChanged(sizeInfo);
        }
        /// <summary>
        /// 重置
        /// </summary>
        private void resetGrid()
        {
            this.BeginInit();
            this.Children.Clear();
            var visual = this.Visual;
            var cols = this.Columns;
            var rows = this.Rows;
            var width = 1d / cols;
            var height = 1d / rows;

            var renderWidth = this.Width / cols;//元素的渲染宽度
            var renderHeight = this.Height / rows;//元素的渲染高度
            for (int c = 0; c < cols; c++)
            {
                for (int r = 0; r < rows; r++)
                {
                    var vb = new VisualBrush(visual)
                    {
                        Viewbox = new Rect(c * width, r * height, width, height),
                        ViewboxUnits = BrushMappingMode.RelativeToBoundingBox
                    };
                    Rectangle rectangle = new Rectangle() { Fill = vb, Stroke = null, StrokeThickness = 0 };
                    SetOriginalPosition(rectangle, new Point(c * renderWidth, r * renderHeight));//设置初始位置
                    rectangle.Width = renderWidth;
                    rectangle.Height = renderHeight;
                    this.Children.Add(rectangle);
                }
            }
            ResetTime();//重置计时器
            this.EndInit();
            this.InvalidateVisual();
        }

        /// <summary>
        /// 开始爆炸
        /// </summary>
        /// <param name="point">爆炸中心</param>
        public void Boom(Point point)
        {
            this.TargetForce = 0;
            this.ReturnForce = 0;
            DragForce = 0;
            two = false;
            resetGrid();
            var cols = this.Columns;
            var rows = this.Rows;


            for (int c = 0; c < cols; c++)
            {
                for (int r = 0; r < rows; r++)
                {
                    int i = r * cols + c;
                    var ele = (Rectangle)this.Children[i];

                    var rect = new Rect(Canvas.GetLeft(ele), Canvas.GetTop(ele), ele.Width, ele.Height);
                    var center = BoomAnimationControl.GetCenter(rect);
                    var v = center - point;
                    var l = v.Length;
                    v.Normalize();
                    v = v * BoomVelocity * random.Next(100, (int)(100 + 100 * BoomRandomSeed)) / 100d;
                    SetVelocity(ele, v);//设置初始速度
                }
            }
            IsRunning = true;
        }
        bool two = false;//第二段动画（往目标点）
        protected override void UpdateChildren(double seconds)
        {
            base.UpdateChildren(seconds);
            if (!two && ElapsedTime > TimeSpan.FromMilliseconds(400))
            {
                two = true;
                foreach (UIElement item in this.Children)
                {
                    var op = GetOriginalPosition(item);
                    SetTargetPosition(item, op + this.NewPlace);
                }
                DragForce = 0.5;
                this.TargetForce = BaseTargetForce;
                //   this.ReturnForce = 50;
            }
            if (two && ElapsedTime.TotalSeconds > 1)
            {
                DragForce = 0.5;
                this.TargetForce -= seconds * 20;
                this.TargetForce = Math.Max(this.TargetForce, 10);
            }

        }

        private Random random = new Random();


    }
}
